// 

#include "at_chat.h"
#include "at_task.h"
#include "module.h"
#include "at_uart.h"
#include <stdbool.h>
#include "cassert.h"
#include "app_user_cfg.h"
#include "at_cmd_table.h"

#include "sha256.h"
//#include "bluenrg_lp_api.h"
#include "ble_const.h"
//#include "gap_profile.h"
#include "gatt_db.h"
#include "app_ble.h"


typedef struct{
  const char* at_cmd;						// 纯命令字符串
	const uint8_t max_para;					// 参数最大允许的长度
	const uint8_t len;					    // 纯命令长度
	const uint8_t id;						// 命令ID号
	const char* descr;					    // 命令描述语句
}at_cmd_table_t; 

/**
 * @brief   
 */
static at_obj_t *at_obj;


const static at_cmd_table_t cmds[] = {
	
	{CMD_DEF(VERSION), .max_para = sizeof("0.9.9") , 
     .descr = "AT+VERSION:\t\t        To check version: \"AT+VERSION?\"   ACK-->: \"AT+VERSION=x.x.x\""},
	{CMD_DEF(NAME),    .max_para = MAX_DEV_LEN,
     .descr = "AT+NAME:\t\t           To check device NAME: \"AT+NAME?\"  To Set device NAME: \"AT+NAME=BlueNRG\""},
	{CMD_DEF(MAC),     .max_para = sizeof("80:EA:CA:00:00:00"),
     .descr = "AT+MAC:\t\t            To check MAC: \"AT+MAC?\"  ACK->: \"AT+MAC=xx:xx:xx:xx:xx:xx\""},
	{CMD_DEF(HELP),    .max_para = 0,
     .descr = "AT+HELP:\t\t           \"AT+HELP?\" or \"AT+HELP\" "},
    {CMD_DEF(ADV_ENABLE),    .max_para = 2,
     .descr = "AT+ADV_ENABLE:\t\t     \"AT+ADV_ENABLE?\" or \"AT+ADV_ENABLE=0\" "},
    {CMD_DEF(SET_ADV_DATA),    .max_para = 31,
     .descr = "AT+SET_ADV_DATA:\t\t   \"AT+SET_ADV_DATA?\" or \"AT+SET_ADV_DATA=0\" "},
    {CMD_DEF(NOTIFY_DATA),    .max_para = 247,
     .descr = "AT+NOTIFY_DATA:\t\t    \"AT+NOTIFY_DATA?\" or \"AT+NOTIFY_DATA=xxxxxx\" "},
    {CMD_DEF(TX_DATA),    .max_para = 247,
     .descr = "AT+TX_DATA:\t\t        \"AT+NOTIFY_DATA=xxxxxx\" "},


	
};



static int help_handler(at_urc_info_t *info)
{
    at_report_singlline(at_obj, "-------------AT CMD Line Help----------");
	for(int i=0;(i<sizeof(cmds)/sizeof(at_cmd_table_t));i++)
	{
		at_report_singlline(at_obj, cmds[i].descr);
	}
	return 0;
}

/* 
 * @brief    
 */
static void at_ver_sender(at_env_t *e)
{
    e->println(e, "AT+VERSION=%s ", APP_VERSION);
}


static void at_res_error(const char* cmd, const char* msg)
{
   at_exec_cmd(at_obj, NULL, "AT+%s:%s ", cmd, msg);
}
/* 
 * @brief   
 * @return  true - 
 */
static int check_version_work(at_env_t *e)
{
    COMPrintf("check_version_work \r\n");
    return 1;
}

static int check_version_handler(at_urc_info_t *info)
{
	at_exec_cmd(at_obj, NULL, "AT+%s=%s ", enumToStr(VERSION), APP_VERSION);
	return 0;
}



/* @brief       检查命令 检查长度
 * @param[in]   cmd_id: 命令ID
 * @param[in]   cmd: 命令和参数字符串
 * @param[in]   cmd_len: 字符串长度  包括\n eg. AT+NAME? ---->AT+NAME\n
 * @param[out]  action: 操作。  0: read  1:set
 * @return     0 - 参数合法,  -1: 输入错误  -2: 长度错误  -3: 
 */

static int at_cmd_check(uint8_t cmd_id, const char* cmd, int cmd_len, uint8_t *action)
{
  int ret = -1;

  int len = cmds[cmd_id].len;
  int param_len = cmd_len - cmds[cmd_id].len;
  int max_para_len = cmds[cmd_id].max_para;
  // 2个字节为赋值符号和结尾的换行符
  if((max_para_len+2  >= param_len)){
	ret = 0;
	// check action
	if(cmd[len] == '?'){
       *action = AT_ACTION_READ;
	}
	else if(cmd[len] == '='){
       if( (cmd_len > len) && (cmd[len+1] == '?')){
	      *action = AT_ACTION_READ;
       }
	   else{
		  *action = AT_ACTION_SET;
	   }
	}
	else{
		*action = AT_ACTION_READ;
	}
}
	

  return ret;
}

// AT+NAME?   AT+NAME=
extern uint8_t dev_name[MAX_DEV_LEN+1];
static int cmd_name_handler(at_urc_info_t *info)
{
    COMPrintf("urcbuf: %s\r\n", info->urcbuf);
	uint8_t action = 0;
	int ret = at_cmd_check(NAME, info->urcbuf, info->urclen, &action);
	if(ret){
		at_res_error(enumToStr(NAME), "Param Error\r\n");
	}
	else{
		if(action == AT_ACTION_SET){
			int param_len = info->urclen - cmds[NAME].len-2;
			if( (param_len > 0) && (param_len <= cmds[NAME].max_para)){
				memset(dev_name,'\0', sizeof(dev_name));
                memcpy(dev_name, info->urcbuf+cmds[NAME].len+1, param_len);
			}
			else{
               at_res_error(enumToStr(NAME), "length Error\r\n");
			   return 0;
			}
		}
        at_exec_cmd(at_obj, NULL, "AT+%s=%s", enumToStr(NAME), dev_name);
	}	

	return 0;
}





static int check_cmd_handler(at_urc_info_t *info)
{
    uint8_t mac[6];
    generate_static_mac_addr(mac);
	  at_exec_cmd(at_obj, NULL, "AT+%s=%02X:%02X:%02X:%02X:%02X:%02X", enumToStr(MAC),\
							mac[5],mac[4],mac[3],mac[2],mac[1],mac[0]);
    return 0;
}



bool at_task_exec_cmd(const char *cmd, ...)
{
    bool ret;
    va_list args;
    va_start(args, cmd);
    ret = at_exec_vcmd(at_obj, NULL, cmd, args);
    va_end(args);
    return ret;
}

/**
 * @brief   
 */
static void at_error(at_response_t *r)
{
    COMPrintf("AT communication error\r\n");
      
}

/**
 * @brief 
 */
static void at_debug(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    //vprintf(fmt, args);
    va_end(args);
}

/** 
 * @brief   AT
 */
static const at_adapter_t  at_adapter = {
    .write         = at_uart_write,
    .read          = at_uart_read,
    .error         = at_error,
    .debug         = at_debug,
    .urc_bufsize   = 128,
    .recv_bufsize  = 256
};
#ifdef ST_USE_OTA_SERVICE_MANAGER_APPLICATION
static int ota_handler(at_urc_info_t *info)
{
    OTA_Jump_To_Service_Manager_Application();
}
#endif
/**
 * @brief   BLE URC table
 */
static const urc_item_t urc_table[] = {
    enumToStr(VERSION),     '\n',      check_version_handler,
    enumToStr(NAME),        '\n',      cmd_name_handler,
    enumToStr(HELP),		'\n',	   help_handler,
    enumToStr(MAC), 		'\n',      check_cmd_handler,
    enumToStr(NOTIFY_DATA), '\n',      notify_data_handler,
#ifdef ST_USE_OTA_SERVICE_MANAGER_APPLICATION
		enumToStr(OTA), '\n',      ota_handler,
#endif
};

/* 
 * @brief    
 */
static void at_cmd_task(void)
{
    at_obj_process(at_obj);
} 

/* 
 * @brief   
 */
static void at_init(void)
{
    at_uart_init();
  
    at_obj = at_obj_create(&at_adapter);
	  app_ble_init(at_obj);
	//
    at_obj_set_urc(at_obj, urc_table, sizeof(urc_table) / sizeof(urc_table[0]));  
    
    COMPrintf("at_init\r\n");

    default_ble_init();
}

driver_init("at_cmd", at_init); 
task_register("at_cmd", at_cmd_task, 0);

